﻿/*
Copyright (c) 2010 Lance Ward
This software is licensed under the MIT license, except where otherwise noted.
For complete license details, refer to license.txt
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using ADNLib;

namespace ADNCanvas
{
    public enum PointEditTarget
    {
        PathMarker,
        BezierControlPoint,
        PathSegment,
        ObjectControlPoint,
        Object,
		None
    }

	public class ToolPointEdit : Tool
	{
		private PointEditTarget target = PointEditTarget.None;
		private List<PathPoint> selPathMarkers = new List<PathPoint>();
		private PointF selObjControlPoint = PointF.Empty;
		private PointF selPathControlPoint = PointF.Empty;
		private int selPathSegment = -1;
        private int selBezierControl = -1;

        public int SelectedSegment
        {
            get { return selPathSegment; }
        }

		public ToolPointEdit() 
        { 
            toolType = DrawingToolType.PointEdit; 
        }

        //only allow one object
        public override List<XObject> SelectedObjects
        {
            get { return base.SelectedObjects; }
            set 
            { 
                base.SelectedObjects = value;
                if (selObjects.Count > 0)
                {
                    XObject o = selObjects[0];
                    selObjects.Clear();
                    selObjects.Add(o);
                }
            }
        }

		public List<PathPoint> SelectedPathMarkers
		{
			get { return selPathMarkers; }
		}

		public override RectangleF EraseRect
		{
			get { return GetEraseRect(); }
		}

        //this is a duplicate of XGroup.Bounds
		private RectangleF GetEraseRect()
		{
			RectangleF rect;

			if (selObjects != null && selObjects.Count > 0 && mode != ToolMode.None)
			{
				Shape firstShape = (Shape)selObjects[0];
				rect = firstShape.Bounds;

				for (int i = 1; i < selObjects.Count; i++)
				{
					Shape s = (Shape)selObjects[i];
					rect = RectangleF.Union(rect, s.Bounds);
				}

				RectangleF prevRect = new RectangleF(prevPoint.X, prevPoint.Y, 0, 0);
                rect = RectangleF.Union(rect, prevRect);

				rect.Inflate(20, 20);
			}
			else if (currentShape != null) { rect = currentShape.Bounds; }
			else { rect = new RectangleF(); }

			return rect;
		}

        public override void MouseDown(PointF location, System.Windows.Forms.MouseButtons button, int clicks)
        {
            int index;
            if (Control.ModifierKeys == Keys.Control)
            {
                index = HitTestObjects(location);
                if (index > -1)
                {
                    selObjects.Clear();
                    selObjects.Add(xObjects[index]);
                    OnSelectionChanged();
                }
            }
            else if (selObjects != null && selObjects.Count > 0)
            {
				Shape s = (Shape)selObjects[0];
                //hit test path markers
                index = HitTestMarkers(location);
				if (index >= 0) 
				{
					ClearSelections();
					selPathMarkers.Add(s.Path.Points[index]);
					prevPoint = location;
					target = PointEditTarget.PathMarker;
					return;
				}

                //hit test object control point
				index = HitTestObjectControlPoint(location);
				if (index >= 0) 
				{ 
					target = PointEditTarget.ObjectControlPoint; 
				}

                //hit test path control points
                index = HitTestBezierControls(location);
				if (index >= 0) 
				{
                    selBezierControl = index;
					target = PointEditTarget.BezierControlPoint; 
                    prevPoint = location;
				}

				//hit test the lines
				index = HitTestPathSegments(location);
				if (index >= 0) 
				{
                    if (selObjects[0].GetType() == typeof(ShapeBezier))
                    {
                        selPathSegment = index;
                        ShapeBezier sb = (ShapeBezier)selObjects[0];
                        sb.InitializeControlPoints(selPathSegment);
                        
                        target = PointEditTarget.PathSegment; 
                    }
				}
            }

        }

		private void ClearSelections()
		{
			selObjControlPoint = PointF.Empty;
			selPathControlPoint = PointF.Empty;
			selPathSegment = -1;
			selPathMarkers.Clear();
		}

        public override void MouseMove(PointF location, System.Windows.Forms.MouseButtons button, int clicks)
        {
			switch (target)
			{
				case PointEditTarget.PathMarker:
					{
						Shape s = (Shape)selObjects[0];
						PointF moveAmount = new PointF(location.X - prevPoint.X, location.Y - prevPoint.Y);
						s.MoveMarker(moveAmount, selPathMarkers[0]);
						prevPoint = location;
						OnRefresh(true);
					}
					break;
				case PointEditTarget.PathSegment:
					{

					}
					break;
				case PointEditTarget.BezierControlPoint:
					{
                        ShapeBezier sb = (ShapeBezier)selObjects[0];
                        PointF moveAmount = new PointF(location.X - prevPoint.X, location.Y - prevPoint.Y);
                        PathPoint p = selBezierControl == 0 ? sb.Path.Points[selPathSegment] : sb.Path.Points[selPathSegment + 1];
                        sb.MoveBezierControl(p, moveAmount, selBezierControl);
                        prevPoint = location;
                        OnRefresh(true);
					}
					break;
				case PointEditTarget.ObjectControlPoint:
					{

					}
					break;
			}
        }

        public override void MouseUp(PointF location, System.Windows.Forms.MouseButtons button, int clicks)
        {
			target = PointEditTarget.None;
        }

        protected override void OnCursorChange(Cursor cursor)
        {
            base.OnCursorChange(cursor);
        }

        protected override void OnRefresh(bool forcePaint)
        {
            base.OnRefresh(forcePaint);
        }

        public override void OnKeyDown(char keyChar, Keys modifiers, bool capsLock)
        {
            if (modifiers == Keys.Delete)
            {
                //delete the currently selected marker or segment
                if (SelectedPathMarkers.Count > 0 && selObjects.Count > 0)
                {
                    if (selObjects[0].GetType() == typeof(ShapeBezier))    //can't delete points from non-path shapes
                    {
                        for (int i = 0; i < SelectedPathMarkers.Count; i++)
                        {
                            Shape s = (Shape)selObjects[0];
                            s.Path.Points.Remove(SelectedPathMarkers[i]);
                        }

                        OnRefresh(true);
                    }
                }
            }
        }

        public override void MouseDoubleClick(PointF location, MouseButtons button, int clicks)
        {
            //split the bezier at this point and insert new point
            if (selObjects.Count > 0 && selObjects[0].GetType() == typeof(ShapeBezier))
            {
                int index = HitTestPathSegments(location);
                if (index >= 0)
                {
                    selPathSegment = index;
                    ShapeBezier sb = (ShapeBezier)selObjects[0];
                    sb.Path.Points.Insert(index + 1, sb.Path.DivideBezier(location, sb.Path.Points[index],
                        sb.Path.Points[index + 1]));

                    OnRefresh(true);
                }
            }
        }

        //TODO: this needs to be moved to Tool, along with the XObjects list
        private void SelectObjects(RectangleF r)
        {
            if (xObjects != null)
            {
                for (int i = xObjects.Count - 1; i >= 0; i--)
                {
                    if (xObjects[i].HitTest(parentSurface, r))
                    {
                        selObjects.Add(xObjects[i]);
                    }
                }
            }
        }

        private int HitTestMarkers(PointF p)
        {
            for (int i = 0; i < selObjects.Count; i++)
            {
                int index = selObjects[i].HitTestPathMarkers(parentSurface, p);
                if (index >= 0) { return index; }
            }

            return -1;
        }

        private int HitTestObjectControlPoint(PointF p)
        {
            for (int i = 0; i < selObjects.Count; i++)
            {
                int index = selObjects[i].HitTestPathMarkers(parentSurface, p);
                if (index >= 0) { return index; }
            }

            return -1;
        }

        private int HitTestBezierControls(PointF p)
        {
            for (int i = 0; i < selObjects.Count; i++)
            {
                if (selObjects[i].GetType() == typeof(ShapeBezier) && selPathSegment >= 0)
                {
                    ShapeBezier sb = (ShapeBezier)selObjects[i];
                    int index = sb.HitTestBezierControls(parentSurface, p, selPathSegment);
                    if (index >= 0) { return index; }
                }
            }

            return -1;
        }

		private int HitTestPathSegments(PointF p)
		{
			for (int i = 0; i < selObjects.Count; i++)
			{
                if (selObjects[i].GetType() == typeof(ShapeBezier))
                {
                    ShapeBezier sb = (ShapeBezier)selObjects[i];
                    int index = sb.HitTestSegment(parentSurface, p);
				    if (index >= 0) { return index; }
                }
			}

			return -1;
		}

        
	}

}
